home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-08-22 | 64.4 KB | 2,400 lines |
- This is a new archive version of TRN at patchlevel 3.
- The original posting took up Volume23, issues 60 to 73, with
- various problems. These files replace those issues.
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # The tool that generated this appeared in the comp.sources.unix newsgroup;
- # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
- # Contents: INIT cheat.c common.h mthreads.c
- # Wrapped by rsalz@litchi.bbn.com on Fri Aug 23 16:38:55 1991
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 6 (of 14)."'
- if test -f 'INIT' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'INIT'\"
- else
- echo shar: Extracting \"'INIT'\" \(18 characters\)
- sed "s/^X//" >'INIT' <<'END_OF_FILE'
- X-ESAMPLE="sample"
- END_OF_FILE
- if test 18 -ne `wc -c <'INIT'`; then
- echo shar: \"'INIT'\" unpacked with wrong size!
- fi
- # end of 'INIT'
- fi
- if test -f 'cheat.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'cheat.c'\"
- else
- echo shar: Extracting \"'cheat.c'\" \(3117 characters\)
- sed "s/^X//" >'cheat.c' <<'END_OF_FILE'
- X/* $Header: cheat.c,v 4.3.3.1 90/07/21 20:14:01 davison Trn $
- X *
- X * $Log: cheat.c,v $
- X * Revision 4.3.3.1 90/07/21 20:14:01 davison
- X * Initial Trn Release
- X *
- X * Revision 4.3.2.2 89/11/27 01:30:18 sob
- X * Altered NNTP code per ideas suggested by Bela Lubkin
- X * <filbo@gorn.santa-cruz.ca.us>
- X *
- X * Revision 4.3.2.1 89/11/26 22:54:21 sob
- X * Added RRN support
- X *
- X * Revision 4.3 85/05/01 11:36:46 lwall
- X * Baseline for release with 4.3bsd.
- X *
- X */
- X
- X#include "EXTERN.h"
- X#include "common.h"
- X#include "intrp.h"
- X#include "search.h"
- X#include "ng.h"
- X#include "bits.h"
- X#include "artio.h"
- X#include "term.h"
- X#include "artsrch.h"
- X#include "head.h"
- X#include "INTERN.h"
- X#include "cheat.h"
- X
- X/* see what we can do while they are reading */
- X
- X#ifdef PENDING
- X# ifdef ARTSEARCH
- X COMPEX srchcompex; /* compiled regex for searchahead */
- X# endif
- X#endif
- X
- Xvoid
- Xcheat_init()
- X{
- X ;
- X}
- X
- X#ifdef PENDING
- Xvoid
- Xlook_ahead()
- X{
- X#ifdef ARTSEARCH
- X register char *h, *s;
- X
- X#ifdef DEBUGGING
- X if (debug && srchahead) {
- X printf("(%ld)",(long)srchahead);
- X fflush(stdout);
- X }
- X#endif
- X if (srchahead && srchahead < art) { /* in ^N mode? */
- X char *pattern;
- X
- X pattern = buf+1;
- X strcpy(pattern,": *");
- X h = pattern + strlen(pattern);
- X interp(h,(sizeof buf) - (h-buf),"%s");
- X h[24] = '\0'; /* compensate for notesfiles */
- X while (*h) {
- X if (index("\\[.^*$'\"",*h) != Nullch)
- X *h++ = '.';
- X else
- X h++;
- X }
- X#ifdef DEBUGGING
- X if (debug & DEB_SEARCH_AHEAD) {
- X fputs("(hit CR)",stdout);
- X fflush(stdout);
- X gets(buf+128);
- X printf("\npattern = %s\n",pattern);
- X }
- X#endif
- X if ((s = compile(&srchcompex,pattern,TRUE,TRUE)) != Nullch) {
- X /* compile regular expression */
- X printf("\n%s\n",s);
- X srchahead = 0;
- X }
- X if (srchahead) {
- X srchahead = art;
- X for (;;) {
- X srchahead++; /* go forward one article */
- X if (srchahead > lastart) { /* out of articles? */
- X#ifdef DEBUGGING
- X if (debug)
- X fputs("(not found)",stdout);
- X#endif
- X break;
- X }
- X if (!was_read(srchahead) &&
- X wanted(&srchcompex,srchahead,0)) {
- X /* does the shoe fit? */
- X#ifdef DEBUGGING
- X if (debug)
- X printf("(%ld)",(long)srchahead);
- X#endif
- X#ifdef SERVER
- X nntpopen(srchahead,GET_HEADER);
- X#else
- X artopen(srchahead);
- X#endif
- X break;
- X }
- X if (input_pending())
- X break;
- X }
- X fflush(stdout);
- X }
- X }
- X else
- X#endif
- X {
- X if (art+1 <= lastart)/* how about a pre-fetch? */
- X#ifdef SERVER
- X nntpopen(art+1,GET_HEADER); /* look for the next article */
- X#else
- X artopen(art+1); /* look for the next article */
- X#endif
- X }
- X}
- X#endif
- X
- X/* see what else we can do while they are reading */
- X
- Xvoid
- Xcollect_subjects()
- X{
- X#ifdef PENDING
- X# ifdef CACHESUBJ
- X ART_NUM oldart = openart;
- X ART_POS oldartpos;
- X
- X if (!in_ng || !srchahead)
- X return;
- X if (oldart) /* remember where we were in art */
- X oldartpos = ftell(artfp);
- X if (srchahead >= subj_to_get)
- X subj_to_get = srchahead+1;
- X while (!input_pending() && subj_to_get <= lastart)
- X fetchsubj(subj_to_get++,FALSE,FALSE);
- X if (oldart) {
- X artopen(oldart);
- X fseek(artfp,oldartpos,0); /* do not screw the pager */
- X }
- X# endif
- X#endif
- X}
- X
- END_OF_FILE
- if test 3117 -ne `wc -c <'cheat.c'`; then
- echo shar: \"'cheat.c'\" unpacked with wrong size!
- fi
- # end of 'cheat.c'
- fi
- if test -f 'common.h' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'common.h'\"
- else
- echo shar: Extracting \"'common.h'\" \(28180 characters\)
- sed "s/^X//" >'common.h' <<'END_OF_FILE'
- X/* $Header: common.h,v 4.3.3.3 91/01/16 02:00:32 davison Trn $
- X *
- X * $Log: common.h,v $
- X * Revision 4.3.3.3 91/01/16 16:28:32 davison
- X * integrated rn patches 48-54 and tweaked defines a bit.
- X *
- X * Revision 4.3.3.2 90/08/20 16:28:32 davison
- X * Tweaked a couple rn's into trn's.
- X *
- X * Revision 4.3.3.1 90/07/21 20:15:23 davison
- X * Initial Trn Release
- X *
- X * Revision 4.3.2.23 90/12/30 23:58:59 sob
- X * Fixed CANCELHEADER.
- X *
- X * Revision 4.3.2.22 90/12/04 02:51:59 sob
- X * removed _SYS_TYPE.H label
- X *
- X * Revision 4.3.2.21 90/11/23 10:39:49 sob
- X * cleaned up more cruft.
- X *
- X * Revision 4.3.2.20 90/11/22 13:18:28 sob
- X * Conditionalized TRUE and FALSE to compensate for AIX which has its own
- X * definitions.
- X *
- X * Revision 4.3.2.19 90/11/09 23:15:12 sob
- X * Added sys/stream.h since sys/ptem.h depends on it.
- X *
- X * Revision 4.3.2.18 90/11/06 00:11:04 sob
- X * Attempt to deal with USG C compilers that can't cope with mutiple
- X * includes of the same include file.
- X *
- X * Revision 4.3.2.17 90/11/05 23:29:37 sob
- X * Added the include of /usr/include/sys/ptem.h if defined in config.h.
- X *
- X * Revision 4.3.2.16 90/11/04 03:34:32 sob
- X * Changed and moved GETWD to Configure.
- X *
- X * Revision 4.3.2.15 90/10/01 02:07:36 sob
- X * Increased LBUFLEN from 512 to 1024 per request of ken@csis.dit.csiro.au.
- X *
- X * Revision 4.3.2.14 90/10/01 01:56:06 sob
- X * Fixed problem with the call to MBOXSAVER reported by news@twwells.com.
- X *
- X * Revision 4.3.2.13 90/05/08 22:05:37 sob
- X * Added quick startup (-q) flag.
- X *
- X * Revision 4.3.2.12 90/04/23 00:32:04 sob
- X * More cleanup.
- X *
- X * Revision 4.3.2.11 90/04/14 19:37:07 sob
- X * Added better support for the NeXT.
- X *
- X * Revision 4.3.2.10 90/04/06 20:54:12 sob
- X * Corrected forward definition of fseek()
- X *
- X * Revision 4.3.2.9 90/03/17 21:19:04 sob
- X * Removed the incorrect forward definition of sprintf().
- X *
- X * Revision 4.3.2.8 89/12/20 20:40:03 sob
- X * Changed ACT_POS from short to long per suggestion from eps@cd.SFSU.EDU.
- X *
- X * Revision 4.3.2.7 89/12/08 22:43:12 sob
- X * Corrected typo pointed out by weening@gang-of-four.stanford.edu
- X *
- X * Revision 4.3.2.6 89/11/28 01:57:31 sob
- X * Added initlines_specified variable for use with SIGWINCH support.
- X *
- X * Revision 4.3.2.5 89/11/28 00:30:56 sob
- X * Reversed the CANCELHEADER definitions.
- X *
- X * Revision 4.3.2.4 89/11/27 01:29:23 sob
- X * Altered NNTP code per ideas suggested by Bela Lubkin
- X * <filbo@gorn.santa-cruz.ca.us>
- X *
- X * Revision 4.3.2.3 89/11/26 19:32:06 sob
- X * Increased the size of MAXRCLINE from 1000 to 1500
- X * Increated HASHSIZ from 1103 to 1693
- X *
- X * Revision 4.3.2.2 89/11/07 23:18:49 sob
- X * Repaired NEWSHEADER and CANCEL to work correctly with NNTP and INTERNET.
- X *
- X * Revision 4.3.2.1 89/11/06 00:12:33 sob
- X * Added RRN support from NNTP 1.5
- X *
- X * Revision 4.3.1.4 86/10/31 15:46:09 lwall
- X * Expanded maximum number of .newsrc lines for net reorganization.
- X *
- X * Revision 4.3.1.3 85/05/23 17:19:32 lwall
- X * Now allows 'r' and 'f' on null articles.
- X *
- X * Revision 4.3.1.2 85/05/13 09:30:39 lwall
- X * Added CUSTOMLINES option.
- X *
- X * Revision 4.3.1.1 85/05/10 11:32:04 lwall
- X * Branch for patches.
- X *
- X * Revision 4.3 85/05/01 11:37:11 lwall
- X * Baseline for release with 4.3bsd.
- X *
- X */
- X
- X#include <stdio.h>
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <ctype.h>
- X#include "config.h" /* generated by installation script */
- X#ifdef WHOAMI
- X# include <whoami.h>
- X#endif
- X#ifndef isalnum
- X# define isalnum(c) (isalpha(c) || isdigit(c))
- X#endif
- X
- X#include <errno.h>
- X#include <signal.h>
- X#ifdef IOCTL
- X#include <sys/ioctl.h>
- X#endif
- X
- X#ifdef FCNTL
- X# include <fcntl.h>
- X#endif
- X
- X#ifdef TERMIO
- X# include <termio.h>
- X#else
- X# include <sgtty.h>
- X#endif
- X
- X#ifdef GETPWENT
- X# include <pwd.h>
- X#endif
- X
- X#ifdef PTEM
- X#include <sys/stream.h>
- X#include <sys/ptem.h>
- X#endif
- X
- X#define BITSPERBYTE 8
- X#ifdef pdp11
- X#define LBUFLEN 512 /* line buffer length */
- X#else
- X#define LBUFLEN 1024 /* line buffer length */
- X#endif
- X /* (don't worry, .newsrc lines can exceed this) */
- X#ifdef pdp11
- X# define CBUFLEN 256 /* command buffer length */
- X# define PUSHSIZE 128
- X#else
- X# define CBUFLEN 512 /* command buffer length */
- X# define PUSHSIZE 256
- X#endif
- X#ifdef pdp11
- X# define MAXFILENAME 128
- X#else
- X# define MAXFILENAME 512
- X#endif
- X#define LONGKEY 15 /* longest keyword: currently "posting-version" */
- X#define FINISHCMD 0177
- X
- X/* some handy defs */
- X
- X#define bool char
- X#ifndef TRUE
- X#define TRUE (1)
- X#endif
- X#ifndef FALSE
- X#define FALSE (0)
- X#endif
- X#define Null(t) ((t)0)
- X#define Nullch Null(char *)
- X#define Nullfp Null(FILE *)
- X
- X#define Ctl(ch) (ch & 037)
- X
- X#define strNE(s1,s2) (strcmp(s1,s2))
- X#define strEQ(s1,s2) (!strcmp(s1,s2))
- X#define strnNE(s1,s2,l) (strncmp(s1,s2,l))
- X#define strnEQ(s1,s2,l) (!strncmp(s1,s2,l))
- X
- X/* Things we can figure out ourselves */
- X
- X#ifdef SIGTSTP
- X# define BERKELEY /* include job control signals? */
- X#endif
- X
- X#ifdef FIONREAD
- X# define PENDING
- X#else
- X# ifdef O_NDELAY
- X# define PENDING
- X# endif
- X#endif
- X
- X#ifdef EUNICE
- X# define LINKART /* add 1 level of possible indirection */
- X# define UNLINK(victim) while (!unlink(victim))
- X#else
- X# define UNLINK(victim) unlink(victim)
- X#endif
- X
- X/* Valid substitutions for strings marked with % comment are:
- X * %a Current article number
- X * %A Full name of current article (%P/%c/%a)
- X * (if LINKART defined, is the name of the real article)
- X * %b Destination of a save command, a mailbox or command
- X * %B The byte offset to the beginning of the article for saves
- X * with or without the header
- X * %c Current newsgroup, directory form
- X * %C Current newsgroup, dot form
- X * %d %P/%c
- X * %D Old Distribution: line
- X * %f Old From: line or Reply-To: line
- X * %F Newsgroups to followup to from Newsgroups: and Followup-To:
- X * %h Name of header file to pass to mail or news poster
- X * %H Host name (yours)
- X * %i Old Message-I.D.: line, with <>
- X * %I Inclusion indicator
- X * %l News administrator login name
- X * %L Login name (yours)
- X * %M Number of articles markd with M
- X * %n Newsgroups from source article
- X * %N Full name (yours)
- X * %o Organization (yours)
- X * %O Original working directory (where you ran rn from)
- X * %p Your private news directory (-d switch)
- X * %P Public news spool directory (SPOOLDIR)
- X * %r Last reference (parent article id)
- X * %R New references list
- X * %s Subject, with all Re's and (nf)'s stripped off
- X * %S Subject, with one Re stripped off
- X * %t New To: line derived from From: and Reply-To (Internet always)
- X * %T New To: line derived from Path:
- X * %u Number of unread articles
- X * %U Number of unread articles disregarding current article
- X * %x News library directory, usually /usr/lib/news
- X * %X Rn library directory, usually %x/rn
- X * %z Size of current article in bytes.
- X * %~ Home directory
- X * %. Directory containing . files
- X * %$ current process number
- X * %{name} Environment variable "name". %{name-default} form allowed.
- X * %[name] Header line beginning with "Name: ", without "Name: "
- X * %"prompt"
- X * Print prompt and insert what is typed.
- X * %`command`
- X * Insert output of command.
- X * %(test_text=pattern?if_text:else_text)
- X * Substitute if_text if test_text matches pattern, otherwise
- X * substitute else_text. Use != for negated match.
- X * % substitutions are done on test_text, if_text, and else_text.
- X * (Note: %() only works if CONDSUB defined.)
- X * %digit Substitute the text matched by the nth bracket in the last
- X * pattern that had brackets. %0 matches the last bracket
- X * matched, in case you had alternatives.
- X *
- X * Put ^ in the middle to capitalize the first letter: %^C = Net.jokes
- X * Put _ in the middle to capitalize last component: %_c = net/Jokes
- X *
- X * ~ interpretation in filename expansion happens after % expansion, so
- X * you could put ~%{NEWSLOGNAME-news} and it will expand correctly.
- X */
- X
- X/* *** System Dependent Stuff *** */
- X
- X/* NOTE: many of these are defined in the config.h file */
- X
- X/* name of organization */
- X#ifndef ORGNAME
- X# define ORGNAME "ACME Widget Company, Widget Falls, Southern North Dakota"
- X#endif
- X
- X#ifndef MBOXCHAR
- X# define MBOXCHAR 'F' /* how to recognize a mailbox by 1st char */
- X#endif
- X
- X#ifndef ROOTID
- X# define ROOTID 0 /* uid of superuser */
- X#endif
- X
- X#ifdef NORMSIG
- X# define sigset signal
- X# define sigignore(sig) signal(sig,SIG_IGN)
- X#endif
- X
- X#ifndef LOGDIRFIELD
- X# define LOGDIRFIELD 6 /* Which field (origin 1) is the */
- X /* login directory in /etc/passwd? */
- X /* (If it is not kept in passwd, */
- X /* but getpwnam() returns it, */
- X /* define the symbol GETPWENT) */
- X#endif
- X#ifndef GCOSFIELD
- X# define GCOSFIELD 5
- X#endif
- X
- X#ifndef NEGCHAR
- X# define NEGCHAR '!'
- X#endif
- X
- X/* Space conservation section */
- X
- X/* To save D space, cut down size of MAXRCLINE, NGMAX, VARYSIZE. */
- X#define MAXRCLINE 1500 /* number of lines allowed in .newsrc */
- X /* several parallel arrays affected. */
- X /* (You can have more lines in the active file, */
- X /* just not in the .newsrc) */
- X#define HASHSIZ 1693 /* should be prime, and at least MAXRCLINE + 10% */
- X#define NGMAX 100 /* number of newsgroups allowed on command line */
- X /* undefine ONLY symbol to disable "only" feature */
- X#define VARYSIZE 256 /* this makes a block 1024 bytes long in DECville */
- X /* (used by virtual array routines) */
- X
- X/* Undefine any of the following features to save both I and D space */
- X/* In general, earlier ones are easier to get along without */
- X/* Pdp11's without split I and D may have to undefine them all */
- X#define DEBUGGING /* include debugging code */
- X#define USETHREADS /* Add article-thread following */
- X#define CUSTOMLINES /* include code for HIDELINE and PAGESTOP */
- X#define PUSHBACK /* macros and keymaps using pushback buffer */
- X#define SPEEDOVERMEM /* use more memory to run faster */
- X#define WORDERASE /* enable ^W to erase a word */
- X#define MAILCALL /* check periodically for mail */
- X#define CLEAREOL /* use clear to end-of-line instead of clear screen */
- X#define NOFIREWORKS /* keep whole screen from flashing on certain */
- X /* terminals such as older Televideos */
- X#define VERIFY /* echo the command they just typed */
- X#define HASHNG /* hash newsgroup lines for fast lookup-- */
- X /* linear search used if not defined */
- X#define CONDSUB /* allow %(cond?text:text) */
- X#define BACKTICK /* allow %`command` */
- X#define PROMPTTTY /* allow %"prompt" */
- X#define ULSMARTS /* catch _^H in text and do underlining */
- X#define TERMMOD /* allow terminal type modifier on switches */
- X#define BAUDMOD /* allow baudrate modifier on switches */
- X#define GETLOGIN /* use getlogin() routine as backup to environment */
- X /* variables USER or LOGNAME */
- X#define ORGFILE /* if organization begins with /, look up in file */
- X#define TILDENAME /* allow ~logname expansion */
- X#define SETENV /* allow command line environment variable setting */
- X#define MAKEDIR /* use our makedir() instead of shell script */
- X#define MEMHELP /* keep help messages in memory */
- X#define VERBOSE /* compile in more informative messages */
- X#define TERSE /* compile in shorter messages */
- X /* (Note: both VERBOSE and TERSE can be defined; -t
- X * sets terse mode. One or the other MUST be defined.
- X */
- X#ifndef pdp11
- X# define CACHESUBJ /* cache subject lines in memory */
- X /* without this ^N still works but runs really slow */
- X /* but you save lots and lots of D space */
- X# define CACHEFIRST /* keep absolute first article numbers in memory */
- X /* cost: about 2k */
- X#endif
- X#define ROTATION /* enable x, X and ^X commands to work */
- X#define DELBOGUS /* ask if bogus newsgroups should be deleted */
- X#define RELOCATE /* allow newsgroup rearranging */
- X#define ESCSUBS /* escape substitutions in multi-character commands */
- X#define DELAYMARK /* allow articles to be temporarily marked as read */
- X /* until exit from current newsgroup or Y command */
- X#define MCHASE /* unmark xrefed articles on m or M */
- X#define MUNGHEADER /* allow alternate header formatting via */
- X /* environment variable ALTHEADER (not impl) */
- X#define ASYNC_PARSE /* allow parsing headers asyncronously to reading */
- X /* used by MCHASE and MUNGHEADER */
- X#define FINDNEWNG /* check for new newsgroups on startup */
- X#define FASTNEW /* do optimizations on FINDNEWNG for faster startup */
- X /* (this optimization can make occasional mistakes */
- X /* if a group is removed and another group of the */
- X /* same length is added, and if no softpointers are */
- X /* affected by said change.) */
- X#define INNERSEARCH /* search command 'g' with article */
- X#define CATCHUP /* catchup command at newsgroup level */
- X#define NGSEARCH /* newsgroup pattern matching */
- X#define ONLY /* newsgroup restrictions by pattern */
- X#define KILLFILES /* automatic article killer files */
- X#define ARTSEARCH /* pattern searches among articles */
- X /* /, ?, ^N, ^P, k, K */
- X
- X/* some dependencies among options */
- X
- X#ifndef ARTSEARCH
- X# undef KILLFILES
- X# undef INNERSEARCH
- X# undef CACHESUBJ
- X#endif
- X
- X#ifndef DELAYMARK
- X# ifndef MCHASE
- X# ifndef MUNGHEADER
- X# undef ASYNC_PARSE
- X# endif
- X# endif
- X#endif
- X
- X#ifndef SETUIDGID
- X# define eaccess access
- X#endif
- X
- X#ifdef ONLY /* idiot lint doesn't grok #if */
- X# define NGSORONLY
- X#else
- X# ifdef NGSEARCH
- X# define NGSORONLY
- X# endif
- X#endif
- X
- X#ifdef VERBOSE
- X# ifdef TERSE
- X# define IF(c) if (c)
- X# define ELSE else
- X# else
- X# define IF(c)
- X# define ELSE
- X# endif
- X#else /* !VERBOSE */
- X# ifndef TERSE
- X# define TERSE
- X# endif
- X# define IF(c) "IF" outside of VERBOSE???
- X# define ELSE "ELSE" outside of VERBOSE???
- X#endif
- X
- X#ifdef DEBUGGING
- X# define assert(ex) {if (!(ex)){fprintf(stderr,"Assertion failed: file %s, line %d\n", __FILE__, __LINE__);sig_catcher(0);}}
- X#else
- X# define assert(ex) ;
- X#endif
- X
- X#ifdef SPEEDOVERMEM
- X# define OFFSET(x) (x)
- X#else
- X# define OFFSET(x) ((x)-absfirst)
- X#endif
- X
- X/* If you're strapped for space use the help messages in shell scripts */
- X/* if {NG,ART,PAGER,SUBS}HELP is undefined, help messages are in memory */
- X#ifdef MEMHELP /* undef MEMHELP above to get them all as sh scripts */
- X# undef NGHELP
- X# undef ARTHELP
- X# undef PAGERHELP
- X# undef SUBSHELP
- X#else
- X# ifndef NGHELP /* % and ~ */
- X# define NGHELP "%X/ng.help"
- X# endif
- X# ifndef ARTHELP /* % and ~ */
- X# define ARTHELP "%X/art.help"
- X# endif
- X# ifndef PAGERHELP /* % and ~ */
- X# define PAGERHELP "%X/pager.help"
- X# endif
- X# ifndef SUBSHELP /* % and ~ */
- X# define SUBSHELP "%X/subs.help"
- X# endif
- X#endif
- X
- X#ifdef CLEAREOL
- X# define TCSIZE 512 /* capacity for termcap strings */
- X#else
- X# ifdef pdp11
- X# define TCSIZE 256 /* capacity for termcap strings */
- X# else
- X# define TCSIZE 512 /* capacity for termcap srings */
- X# endif
- X#endif
- X
- X/* Additional ideas:
- X * Make the do_newsgroup() routine a separate process.
- X * Keep .newsrc on disk instead of in memory.
- X * Overlays, if you have them.
- X * Get a bigger machine.
- X */
- X
- X/* End of Space Conservation Section */
- X
- X/* More System Dependencies */
- X
- X/* news library */
- X#ifndef LIB /* ~ and %l only ("~%l" is permissable) */
- X# define LIB "/usr/lib/news"
- X#endif
- X
- X/* path to private executables */
- X#ifndef RNLIB /* ~, %x and %l only */
- X# define RNLIB "%x/trn"
- X#endif
- X
- X/* system-wide RNINIT switches */
- X#ifndef GLOBINIT
- X# define GLOBINIT "%X/INIT"
- X#endif
- X
- X/* where to find news files */
- X#ifndef SPOOL /* % and ~ */
- X# define SPOOL "/usr/spool/news"
- X#endif
- X
- X#ifdef USETHREADS
- X# ifdef THREAD_DIR
- X# ifdef LONG_THREAD_NAMES
- X# undef SUFFIX
- X# else
- X# ifndef SUFFIX
- X# define SUFFIX ".th"
- X# endif
- X# endif
- X# else
- X# define THREAD_DIR SPOOL
- X# ifndef SUFFIX
- X# define SUFFIX "/.thread"
- X# endif
- X# undef LONG_THREAD_NAMES
- X# endif
- X# ifndef NEW_THREAD
- X# define NEW_THREAD ".new"
- X# endif
- X#endif
- X
- X/* default characters to use in the selection menu */
- X#ifndef SELECTCHARS
- X# define SELECTCHARS "abcdefgijlorstuvwxz1234567890"
- X#endif
- X
- X/* file containing list of active newsgroups and max article numbers */
- X#ifndef ACTIVE /* % and ~ */
- X# define ACTIVE "%x/active"
- X#endif
- X#ifdef SERVER
- X# ifndef ACTIVE1
- X# define ACTIVE1 "%X/active1"
- X# endif
- X#endif
- X#ifndef ACTIVE2
- X# define ACTIVE2 "%X/active2"
- X#endif
- X
- X/* location of history file */
- X#ifndef ARTFILE /* % and ~ */
- X# define ARTFILE "%x/history"
- X#endif
- X
- X/* command to setup a new .newsrc */
- X#ifndef NEWSETUP /* % and ~ */
- X# define NEWSETUP "newsetup"
- X#endif
- X
- X/* command to display a list of un-subscribed-to newsgroups */
- X#ifndef NEWSGROUPS /* % and ~ */
- X# define NEWSGROUPS "newsgroups"
- X#endif
- X
- X/* preferred shell for use in doshell routine */
- X/* ksh or sh would be okay here */
- X#ifndef PREFSHELL
- X# define PREFSHELL "/bin/csh"
- X#endif
- X
- X/* path to fastest starting shell */
- X#ifndef SH
- X# define SH "/bin/sh"
- X#endif
- X
- X/* default unshar'ing program */
- X#ifndef UNSHAR
- X# define UNSHAR "/bin/sh"
- X#endif
- X
- X/* path to default editor */
- X#ifndef DEFEDITOR
- X# define DEFEDITOR "/usr/ucb/vi"
- X#endif
- X
- X/* location of macro file */
- X#ifndef RNMACRO
- X# ifdef PUSHBACK
- X# define RNMACRO "%./.rnmac"
- X# endif
- X#endif
- X
- X/* location of full name */
- X#ifndef FULLNAMEFILE
- X# ifndef PASSNAMES
- X# define FULLNAMEFILE "%./.fullname"
- X# endif
- X#endif
- X
- X/* virtual array file name template */
- X#ifndef VARYNAME /* % and ~ */
- X# define VARYNAME "/tmp/rnvary.%$"
- X#endif
- X
- X/* where to compile a new newsgroup list */
- X#ifndef RNEWNAME
- X# define RNEWNAME "/tmp/rnew.%$"
- X#endif
- X
- X/* file to pass header to followup article poster */
- X#ifndef HEADNAME /* % and ~ */
- X# define HEADNAME "%./.rnhead"
- X/* or alternately #define HEADNAME "/tmp/rnhead.%$" */
- X#endif
- X
- X#ifndef MAKEDIR
- X/* shell script to make n-deep subdirectories */
- X# ifndef DIRMAKER /* % and ~ */
- X# define DIRMAKER "%X/makedir"
- X# endif
- X#endif
- X
- X/* location of newsrc file */
- X#ifndef RCNAME /* % and ~ */
- X# define RCNAME "%./.newsrc"
- X#endif
- X
- X/* temporary newsrc file in case we crash while writing out */
- X#ifndef RCTNAME /* % and ~ */
- X# define RCTNAME "%./.newnewsrc"
- X#endif
- X
- X/* newsrc file at the beginning of this session */
- X#ifndef RCBNAME /* % and ~ */
- X# define RCBNAME "%./.oldnewsrc"
- X#endif
- X
- X/* if existent, contains process number of current or crashed rn */
- X#ifndef LOCKNAME /* % and ~ */
- X# define LOCKNAME "%./.rnlock"
- X#endif
- X
- X/* information from last invocation of rn */
- X#ifndef LASTNAME /* % and ~ */
- X# define LASTNAME "%./.rnlast"
- X#endif
- X
- X/* file with soft pointers into the active file */
- X#ifndef SOFTNAME /* % and ~ */
- X# define SOFTNAME "%./.rnsoft"
- X#endif
- X
- X/* list of article numbers to mark as unread later (see M and Y cmmands) */
- X#ifndef RNDELNAME /* % and ~ */
- X# define RNDELNAME "%./.rndelay"
- X#endif
- X
- X/* a motd-like file for rn */
- X#ifndef NEWSNEWSNAME /* % and ~ */
- X# define NEWSNEWSNAME "%X/newsnews"
- X#endif
- X
- X/* command to send a reply */
- X#ifndef MAILPOSTER /* % and ~ */
- X# define MAILPOSTER "Rnmail -h %h"
- X#endif
- X
- X#ifdef INTERNET
- X# ifndef MAILHEADER /* % */
- X# ifdef CONDSUB
- X# define MAILHEADER "To: %t\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n"
- X# else
- X# define MAILHEADER "To: %t\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n"
- X# endif
- X# endif
- X#else
- X# ifndef MAILHEADER /* % */
- X# ifdef CONDSUB
- X# define MAILHEADER "To: %T\nSubject: %(%i=^$?:Re: %S\nNewsgroups: %n\nIn-Reply-To: %i)\n%(%[references]!=^$?References\\: %[references]\n)Organization: %o\nCc: \nBcc: \n\n"
- X# else
- X# define MAILHEADER "To: %T\nSubject: Re: %S\nNewsgroups: %n\nIn-Reply-To: %i\nReferences: %[references]\nCc: \nBcc: \n\n"
- X# endif
- X# endif
- X#endif
- X
- X#ifndef YOUSAID /* % */
- X# define YOUSAID "In article %i you write:"
- X#endif
- X
- X/* command to submit a followup article */
- X#ifndef NEWSPOSTER /* % and ~ */
- X# define NEWSPOSTER "Pnews -h %h"
- X#endif
- X
- X#ifndef NEWSHEADER /* % */
- X# ifdef CONDSUB
- X#ifdef INTERNET
- X# define NEWSHEADER "Newsgroups: %(%F=^$?%C:%F)\nSubject: %(%S=^$?%\"\n\nSubject: \":Re: %S)\nSummary: \nExpires: \n%(%R=^$?:References: %R\n)Sender: \nFollowup-To: \nDistribution: %(%i=^$?%\"Distribution: \":%D)\nOrganization: %o\nKeywords: %[keywords]\n\n"
- X#else
- X# define NEWSHEADER "Newsgroups: %(%F=^$?%C:%F)\nSubject: %(%S=^$?%\"\n\nSubject: \":Re: %S)\nSummary: \nExpires: \n%(%R=^$?:References: %R\n)Sender: \nFollowup-To: \nDistribution: %(%i=^$?%\"Distribution: \":%D)\nOrganization: %o\nKeywords: %[keywords]\n\n"
- X#endif
- X# else
- X# ifdef INTERNET
- X# define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n"
- X# else
- X# define NEWSHEADER "Newsgroups: %F\nSubject: Re: %S\nSummary: \nExpires: \nReferences: %R\nSender: \nFollowup-To: \nDistribution: %D\nOrganization: %o\nKeywords: %[keywords]\n\n"
- X# endif
- X# endif
- X#endif
- X
- X#ifndef ATTRIBUTION /* % */
- X# define ATTRIBUTION "In article %i %f writes:"
- X#endif
- X
- X#ifndef PIPESAVER /* % */
- X# ifdef CONDSUB
- X# ifdef SERVER
- X# define PIPESAVER "%(%B=^0$?<%P/rrn%a.%$:tail +%Bc %P/rrn%a.%$ |) %b"
- X# else
- X# define PIPESAVER "%(%B=^0$?<%A:tail +%Bc %A |) %b"
- X# endif
- X# else
- X# ifdef SERVER
- X# define PIPESAVER "tail +%Bc %P/rrn%a.%$ | %b"
- X# else
- X# define PIPESAVER "tail +%Bc %A | %b"
- X# endif
- X# endif
- X#endif
- X
- X#ifndef EXSAVER
- X# ifdef SERVER
- X# define EXSAVER "tail +%Bc %P/rrn%a.%$ | %e"
- X# else
- X# define EXSAVER "tail +%Bc %A | %e"
- X# endif
- X#endif
- X
- X#ifndef NORMSAVER /* % and ~ */
- X# ifdef SERVER
- X# define NORMSAVER "%X/norm.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\""
- X# else
- X# define NORMSAVER "%X/norm.saver %A %P %c %a %B %C \"%b\""
- X# endif
- X#endif
- X
- X#ifndef MBOXSAVER /* % and ~ */
- X# ifdef MININACT /* 2.10.2 site? */
- X# ifdef SERVER
- X# define MBOXSAVER "%X/mbox.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\" \"From %T %`date`\""
- X# else
- X# define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %`date`\""
- X# endif
- X# else
- X# ifdef CONDSUB
- X# ifdef SERVER
- X# define MBOXSAVER "%X/mbox.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\" \"From %T %(%[date]=^\\(\\w*\\), \\(\\w*\\)-\\(\\w*\\)-\\(\\w*\\) \\([^ ]*\\)?%1 %3 %(%2=..?%2: %2) %5 19%4)\""
- X# else
- X# define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %(%[date]=^\\(\\w*\\), \\(\\w*\\)-\\(\\w*\\)-\\(\\w*\\) \\([^ ]*\\)?%1 %3 %(%2=..?%2: %2) %5 19%4)\""
- X# endif
- X /* header munging with a vengeance */
- X# else
- X# ifdef SERVER
- X# define MBOXSAVER "%X/mbox.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\" \"From %T %[posted]\""
- X# else
- X# define MBOXSAVER "%X/mbox.saver %A %P %c %a %B %C \"%b\" \"From %T %[posted]\""
- X# endif
- X# endif
- X# endif
- X#endif
- X
- X#ifdef MKDIRS
- X
- X# ifndef SAVEDIR /* % and ~ */
- X# define SAVEDIR "%p/%c"
- X# endif
- X# ifndef SAVENAME /* % */
- X# define SAVENAME "%a"
- X# endif
- X
- X#else
- X
- X# ifndef SAVEDIR /* % and ~ */
- X# define SAVEDIR "%p"
- X# endif
- X# ifndef SAVENAME /* % */
- X# define SAVENAME "%^C"
- X# endif
- X
- X#endif
- X
- X#ifndef KILLGLOBAL /* % and ~ */
- X# define KILLGLOBAL "%p/KILL"
- X#endif
- X
- X#ifndef KILLLOCAL /* % and ~ */
- X# define KILLLOCAL "%p/%c/KILL"
- X#endif
- X
- X/* how to cancel an article */
- X#ifndef CANCEL
- X# ifdef MININACT /* 2.10.2 ? */
- X# define CANCEL "%x/inews -h < %h"
- X# else
- X# define CANCEL "inews -h < %h"
- X# endif
- X#endif
- X
- X/* how to cancel an article, continued */
- X#ifndef CANCELHEADER
- X#ifdef INTERNET
- X# define CANCELHEADER "From: %L@%H (%N)\nNewsgroups: %n\nSubject: cmsg cancel %i\nReferences: %R\nDistribution: %D\nOrganization: %o\n\nThis message was cancelled from within trn.\n"
- X#else
- X# define CANCELHEADER "From:%L@%H.UUCP (%N)\nNewsgroups: %n\nSubject: cmsg cancel %i\nReferences: %R\nDistribution: %D\nOrganization: %o\n"
- X#endif
- X#endif
- X
- X/* where to find the mail file */
- X#ifndef MAILFILE
- X# define MAILFILE "/usr/spool/mail/%L"
- X#endif
- X
- X/* how to open binary format files */
- X#ifndef FOPEN_RB
- X# define FOPEN_RB "r"
- X#endif
- X#ifndef FOPEN_WB
- X# define FOPEN_WB "w"
- X#endif
- X
- X/* what to do with ansi prototypes -- '()' == ignore, 'x' == use */
- X#ifndef ANSI
- X# define ANSI(x) ()
- X#endif
- X
- X/* how many characters is a newline in a text file? */
- X#ifndef NL_SIZE
- X# define NL_SIZE 1
- X#endif
- X
- X/* some important types */
- X
- Xtypedef int NG_NUM; /* newsgroup number */
- Xtypedef long ART_NUM; /* article number */
- X#ifdef pdp11
- X typedef short ART_UNREAD; /* ordinarily this should be long */
- X /* like ART_NUM, but assuming that */
- X /* we stay less than 32767 articles */
- X /* behind saves a lot of space. */
- X /* NOTE: do not make unsigned. */
- X#else
- X typedef long ART_UNREAD;
- X#endif
- X#ifdef SERVER
- Xtypedef int ART_PART; /* for passing to nntpopen() */
- X#endif
- Xtypedef long ART_POS; /* char position in article file */
- Xtypedef int ART_LINE; /* line position in article file */
- Xtypedef long ACT_POS; /* char position in active file */
- Xtypedef unsigned int MEM_SIZE; /* for passing to malloc */
- X
- X
- X/* *** end of the machine dependent stuff *** */
- X
- X/* GLOBAL THINGS */
- X
- X/* file statistics area */
- X
- XEXT struct stat filestat;
- X
- X/* various things of type char */
- X
- Xchar *index();
- Xchar *rindex();
- Xchar *getenv();
- Xchar *strcat();
- Xchar *strcpy();
- X
- XEXT char buf[LBUFLEN+1]; /* general purpose line buffer */
- XEXT char cmd_buf[CBUFLEN]; /* buffer for formatting system commands */
- X
- XEXT char *indstr INIT(">"); /* indent for old article embedded in followup */
- X
- XEXT char *cwd INIT(Nullch); /* current working directory */
- XEXT char *dfltcmd INIT(Nullch); /* 1st char is default command */
- X
- X/* switches */
- X
- X#ifdef DEBUGGING
- X EXT int debug INIT(0); /* -D */
- X# define DEB_INNERSRCH 32
- X# define DEB_FILEXP 64
- X# define DEB_HASH 128
- X# define DEB_XREF_MARKER 256
- X# define DEB_CTLAREA_BITMAP 512
- X# define DEB_SOFT_POINTERS 1024
- X# define DEB_NEWSRC_LINE 2048
- X# define DEB_SEARCH_AHEAD 4096
- X# define DEB_CHECKPOINTING 8192
- X# define DEB_FEED_XREF 16384
- X#endif
- X
- X#ifdef ARTSEARCH
- X EXT int scanon INIT(0); /* -S */
- X#endif
- X
- X#ifdef USETHREADS
- X EXT bool use_threads INIT(THREAD_INIT); /* -x */
- X EXT int max_tree_lines INIT(6);
- X EXT char select_order[4] INIT("lsm");
- X EXT int select_on INIT(SELECT_INIT); /* -X */
- X EXT char end_select INIT('Z');
- X EXT char page_select INIT('>');
- X#endif
- X
- XEXT bool mbox_always INIT(FALSE); /* -M */
- XEXT bool norm_always INIT(FALSE); /* -N */
- XEXT bool checkflag INIT(FALSE); /* -c */
- XEXT bool suppress_cn INIT(FALSE); /* -s */
- XEXT int countdown INIT(5); /* how many lines to list before invoking -s */
- XEXT bool muck_up_clear INIT(FALSE); /* -loco */
- XEXT bool erase_screen INIT(FALSE); /* -e */
- X#if defined(CLEAREOL) || defined(USETHREADS)
- XEXT bool can_home INIT(FALSE);
- X#endif
- X#ifdef CLEAREOL
- XEXT bool can_home_clear INIT(FALSE); /* fancy -e */
- X#endif
- XEXT bool findlast INIT(FALSE); /* -r */
- XEXT bool typeahead INIT(FALSE); /* -T */
- X#ifdef VERBOSE
- X# ifdef TERSE
- X EXT bool verbose INIT(TRUE); /* +t */
- X# endif
- X#endif
- X#ifdef VERIFY
- X EXT bool verify INIT(FALSE); /* -v */
- X#endif
- X EXT bool quickstart INIT(FALSE); /* -q */
- X
- X#define NOMARKING 0
- X#define STANDOUT 1
- X#define UNDERLINE 2
- XEXT int marking INIT(NOMARKING); /* -m */
- X
- XEXT ART_LINE initlines INIT(0); /* -i */
- XEXT bool initlines_specified INIT(FALSE);
- X
- X/* miscellania */
- X
- Xint fseek();
- Xlong atol(), ftell();
- XEXT bool in_ng INIT(FALSE); /* current state of rn */
- XEXT char mode INIT('i'); /* current state of rn */
- X
- XEXT FILE *tmpfp INIT(Nullfp); /* scratch fp used for .rnlock, .rnlast, etc. */
- X
- XEXT NG_NUM nextrcline INIT(0); /* 1st unused slot in rcline array */
- X /* startup to avoid checking twice in a row */
- X
- Xextern errno;
- X
- X/* Factored strings */
- X
- XEXT char nullstr[] INIT("");
- XEXT char sh[] INIT(SH);
- XEXT char defeditor[] INIT(DEFEDITOR);
- XEXT char hforhelp[] INIT("Type h for help.\n");
- X#ifdef STRICTCR
- XEXT char badcr[] INIT("\nUnnecessary CR ignored.\n");
- X#endif
- XEXT char readerr[] INIT("rn read error");
- XEXT char unsubto[] INIT("\n\nUnsubscribed to newsgroup %s\n");
- XEXT char cantopen[] INIT("Can't open %s\n");
- XEXT char cantcreate[] INIT("Can't create %s\n");
- X
- X#ifdef VERBOSE
- X EXT char nocd[] INIT("Can't chdir to directory %s\n");
- X#else
- X EXT char nocd[] INIT("Can't find %s\n");
- X#endif
- X
- X#ifdef NOLINEBUF
- X#define FLUSH ,fflush(stdout)
- X#else
- X#define FLUSH
- X#endif
- X
- X#ifdef lint
- X#undef FLUSH
- X#define FLUSH
- X#undef putchar
- X#define putchar(c)
- X#endif
- END_OF_FILE
- if test 28180 -ne `wc -c <'common.h'`; then
- echo shar: \"'common.h'\" unpacked with wrong size!
- fi
- # end of 'common.h'
- fi
- if test -f 'mthreads.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'mthreads.c'\"
- else
- echo shar: Extracting \"'mthreads.c'\" \(29761 characters\)
- sed "s/^X//" >'mthreads.c' <<'END_OF_FILE'
- X/* $Header: mthreads.c,v 4.3.3.3 91/01/18 19:05:00 davison Trn $
- X**
- X** $Log: mthreads.c,v $
- X** Revision 4.3.3.3 91/01/18 19:05:00 davison
- X** Modified the way signals are handled to avoid endless loops. Added -s & -z
- X** options. Fixed a truncate bug and a problem with new groups not processing.
- X**
- X** Revision 4.3.3.2 90/08/20 16:43:19 davison
- X** Implemented new command-line interface and database upgrading.
- X**
- X** Revision 4.3.3.1 90/07/24 22:24:17 davison
- X** Initial Trn Release
- X**
- X*/
- X
- X/* mthreads.c -- for making and updating a discussion-thread database
- X**
- X** We use the active file as our high/low counts for each group, and create
- X** an active2 file of our own to keep track of the high/lows of the database.
- X** When fully updated, the two files should be identical. This gives us
- X** quick access to the last processed high/low counts without opening
- X** each data file, PLUS it allows trn to use the fake active file as if it
- X** were the real thing to keep it from seeing articles before they are
- X** processed. If the active2 file is removed or corrupted, it will be
- X** automatically repaired in the normal course of operation. We update
- X** the file IN PLACE so that trn can keep it open all the time. Normally
- X** the size of the file does not change, so it is easy to do. In those
- X** rare instances where a news admin shuffles the real active file, we
- X** take it all in stride by throwing a little memory at the problem.
- X**
- X** Usage: mthreads [-d[MM]] [-e[HHMM]] [-aDfknv] [hierarchy_list]
- X*/
- X
- X#include "EXTERN.h"
- X#include "common.h"
- X#ifdef SERVER
- X#include "server.h"
- X#endif
- X#include "INTERN.h"
- X#include "mthreads.h"
- X
- X#ifdef TZSET
- X#include <time.h>
- X#else
- X#include <sys/time.h>
- X#include <sys/timeb.h>
- X#endif
- X
- XFILE *fp_lock, *fp_log;
- X
- Xstruct stat filestat;
- X
- Xstatic char line[256];
- Xstatic char line2[256];
- X
- X/* If you want to change the field size, do it once and then leave it alone. */
- Xchar fmt_active2[] = "%s %06ld %06ld %c\n";
- X
- Xchar *filename;
- X
- Xtypedef struct _active_line {
- X struct _active_line *link;
- X char *name;
- X long last;
- X long first;
- X char type;
- X} ACTIVE_LINE;
- X
- X#define Nullact Null(ACTIVE_LINE*)
- X
- XACTIVE_LINE *line_root = Nullact, *last_line = Nullact, *pline = Nullact;
- X
- Xbool force_flag = FALSE, kill_mthreads = FALSE, no_processing = FALSE;
- Xbool add_new = FALSE, rebuild = FALSE, zap_thread = FALSE, grevious_error;
- Xint daemon_delay = 0, log_verbosity = 0, debug = 0, slow_down = 0;
- Xlong expire_time = 0;
- Xchar *hierarchy_list = NULL;
- Xlong truncate_len = -1;
- X
- Xchar nullstr[] = "";
- X
- XBMAP my_bmap, mt_bmap;
- X
- X#ifdef TZSET
- Xtime_t tnow;
- X#else
- Xstruct timeb ftnow;
- X#endif
- X
- X#define TIMER_FIRST 1
- X#define TIMER_DEFAULT (10 * 60)
- X
- Xint added_groups, removed_groups, action;
- X
- X#define NG_DEFAULT 0
- X#define NG_MATCH 1
- X#define NG_SKIP 2
- X
- X#ifdef SERVER
- Xchar *server;
- X#else
- Xtime_t last_modified;
- X#endif
- X
- XSIGRET alarm_handler(), int_handler(), severe_handler();
- Xvoid makethreads(), wrap_it_up();
- X
- Xmain( argc, argv )
- Xint argc;
- Xchar *argv[];
- X{
- X int fd;
- X long pid;
- X
- X while( --argc ) {
- X if( **++argv == '-' ) {
- X while( *++*argv ) {
- X switch( **argv ) {
- X case 'a': /* automatically thread new groups */
- X add_new = TRUE;
- X break;
- X case 'D':
- X debug++;
- X break;
- X case 'd':
- X if( *++*argv <= '9' && **argv >= '0' ) {
- X daemon_delay = atoi( *argv ) * 60;
- X while( *++*argv <= '9' && **argv >= '0' ) {
- X ;
- X }
- X } else {
- X daemon_delay = TIMER_DEFAULT;
- X }
- X --*argv;
- X break;
- X case 'e': {
- X struct tm *ts;
- X long desired;
- X
- X (void) time( &expire_time );
- X ts = localtime( &expire_time );
- X
- X if( *++*argv <= '9' && **argv >= '0' ) {
- X desired = atol( *argv );
- X if( desired/100 > 23 || desired%100 > 59 ) {
- X fprintf( stderr, "Illegal expire time: '%04d'\n",
- X desired );
- X exit( 1 );
- X }
- X desired = (desired/100)*60 + desired%100;
- X while( *++*argv <= '9' && **argv >= '0' ) {
- X ;
- X }
- X } else {
- X desired = 30; /* 0030 = 12:30am */
- X }
- X --*argv;
- X desired -= ts->tm_hour * 60 + ts->tm_min;
- X if( desired < 0 ) {
- X desired += 24 * 60;
- X }
- X expire_time += desired * 60 - ts->tm_sec;
- X break;
- X }
- X case 'f':
- X force_flag = TRUE;
- X break;
- X case 'k':
- X kill_mthreads = TRUE;
- X break;
- X case 'n':
- X no_processing = TRUE;
- X break;
- X case 's':
- X slow_down++;
- X break;
- X case 'v':
- X log_verbosity++;
- X break;
- X case 'z':
- X zap_thread = TRUE;
- X break;
- X default:
- X fprintf( stderr, "Unknown option: '%c'\n", **argv );
- X exit( 1 );
- X }
- X }
- X } else {
- X if( hierarchy_list ) {
- X fprintf( stderr, "Specify the newsgroups in one comma-separated list.\n" );
- X exit( 1 );
- X }
- X hierarchy_list = *argv;
- X }
- X }
- X
- X /* Set up a nice friendly umask. */
- X umask( 002 );
- X
- X /* What time is it? */
- X#ifdef TZSET
- X (void) time( &tnow );
- X (void) tzset();
- X#else
- X (void) ftime( &ftnow );
- X#endif
- X
- X /* Make sure we're not already running by creating a lock file.
- X ** (I snagged this method from C news.)
- X */
- X sprintf( line, "%s.%d", file_exp( "%X/LOCK" ), getpid() );
- X if( (fp_lock = fopen( line, "w" )) == Nullfp ) {
- X fprintf( stderr, "Unable to create lock temporary `%s'.\n", line );
- X exit( 1 );
- X }
- X fprintf( fp_lock, "%d\n", getpid() );
- X fclose( fp_lock );
- X
- X /* Try to link to lock file. */
- X filename = file_exp( "%X/LOCKmthreads" );
- X dolink:
- X if( link( line, filename ) < 0 ) {
- X long otherpid;
- X /* Try to avoid possible race with daemon starting up. */
- X sleep (5);
- X if( (fp_lock = fopen( filename, "r")) == Nullfp ) {
- X fprintf( stderr, "unable to open %s\n", filename );
- X unlink( line );
- X exit( 1 );
- X }
- X if( fscanf( fp_lock, "%ld", &otherpid ) != 1) {
- X fprintf( stderr, "unable to read pid from %s\n", filename );
- X unlink( line );
- X fclose( fp_lock );
- X exit( 1 );
- X }
- X fclose( fp_lock );
- X if( kill( otherpid, kill_mthreads ? SIGTERM : 0 ) == -1
- X && errno == ESRCH ) {
- X if( unlink( filename ) == -1 ) {
- X fprintf( stderr, "unable to unlink lockfile %s\n", filename );
- X unlink( line );
- X exit( 1 );
- X }
- X if( !kill_mthreads ) {
- X goto dolink;
- X }
- X }
- X unlink( line );
- X if( kill_mthreads ) {
- X fprintf( stderr, "killing currently running mthreads.\n" );
- X exit( 0 );
- X } else {
- X fprintf( stderr, "mthreads is already running.\n" );
- X exit( 1 );
- X }
- X }
- X
- X unlink( line ); /* remove temporary LOCK.<pid> file */
- X
- X if( kill_mthreads ) {
- X fprintf( stderr, "mthreads is not running.\n" );
- X exit( 1 );
- X }
- X
- X /* Open our log file */
- X filename = file_exp( "%X/mt.log" );
- X if( (fp_log = fopen( filename, "a" )) == Nullfp ) {
- X fprintf( stderr, "Unable to open `%s'.\n", filename );
- X exit( 1 );
- X }
- X
- X#ifdef SIGHUP
- X if( sigset( SIGHUP, SIG_IGN ) != SIG_IGN ) {
- X sigset( SIGHUP, int_handler );
- X }
- X#endif
- X if( sigset( SIGINT, SIG_IGN ) != SIG_IGN ) {
- X sigset( SIGINT, int_handler );
- X }
- X#ifdef SIGQUIT
- X if( sigset( SIGQUIT, SIG_IGN ) != SIG_IGN ) {
- X sigset( SIGQUIT, int_handler );
- X }
- X#endif
- X sigset( SIGTERM, int_handler );
- X#ifdef SIGBUS
- X sigset( SIGBUS, severe_handler );
- X#endif
- X sigset( SIGSEGV, severe_handler );
- X#ifdef SIGTTIN
- X sigset( SIGTTIN, SIG_IGN );
- X sigset( SIGTTOU, SIG_IGN );
- X#endif
- X sigset( SIGALRM, SIG_IGN );
- X#ifdef lint
- X alarm_handler(); /* foolishness for lint's sake */
- X int_handler( SIGINT );
- X severe_handler( SIGSEGV );
- X#endif
- X
- X /* Ensure this machine has the right byte-order for the database */
- X filename = file_exp( "%X/db.init" );
- X if( (fp_lock = fopen( filename, FOPEN_RB )) == Nullfp
- X || fread( &mt_bmap, 1, sizeof (BMAP), fp_lock ) < sizeof (BMAP)-1 ) {
- X if( fp_lock != Nullfp ) {
- X fclose( fp_lock );
- X }
- X write_db_init:
- X mybytemap( &mt_bmap );
- X if( (fp_lock = fopen( filename, FOPEN_WB )) == Nullfp ) {
- X log_entry( "Unable to create file: `%s'.\n", filename );
- X exit( 1 );
- X }
- X mt_bmap.version = DB_VERSION;
- X fwrite( &mt_bmap, 1, sizeof (BMAP), fp_lock );
- X fclose( fp_lock );
- X } else {
- X int i;
- X
- X fclose( fp_lock );
- X if( mt_bmap.version != DB_VERSION ) {
- X if( mt_bmap.version == DB_VERSION-1 ) {
- X rebuild = TRUE;
- X log_entry( "Upgrading database to version %d.\n", DB_VERSION );
- X goto write_db_init;
- X }
- X log_entry( "** Database is not the right version (%d instead of %d) **\n",
- X mt_bmap.version, DB_VERSION );
- X exit( 1 );
- X }
- X mybytemap( &my_bmap );
- X for( i = 0; i < sizeof (LONG); i++ ) {
- X if( my_bmap.l[i] != mt_bmap.l[i]
- X || (i < sizeof (WORD) && my_bmap.w[i] != mt_bmap.w[i]) ) {
- X log_entry( "\
- X** Byte-order conflict -- re-run from a compatible machine **\n\
- X\t\tor remove the current thread files, including db.init **\n" );
- X exit( 1 );
- X }
- X }
- X }
- X
- X#ifdef SERVER
- X server = getserverbyfile( SERVER_FILE );
- X if( server == NULL ) {
- X log_entry( "Couldn't find name of news server.\n" );
- X exit( 1 );
- X }
- X#endif
- X
- X /* If we're not in daemon mode, run through once and quit. */
- X if( !daemon_delay ) {
- X log_entry( "mthreads single pass started.\n" );
- X setbuf( stdout, Nullch );
- X extra_expire = (expire_time != 0);
- X makethreads();
- X } else {
- X /* For daemon mode, we cut ourself off from anything tty-related and
- X ** run in the background (involves forks, but no knives).
- X */
- X close( 0 );
- X if( open( "/dev/null", 2 ) != 0 ) {
- X fprintf( stderr, "unable to open /dev/null!\n" );
- X exit( 1 );
- X }
- X close( 1 );
- X close( 2 );
- X dup( 0 );
- X dup( 0 );
- X while( (pid = fork()) < 0 ) {
- X sleep( 2 );
- X }
- X if( pid ) {
- X exit( 0 );
- X }
- X#ifdef TIOCNOTTY
- X if( (fd = open( "/dev/tty", 1 )) >= 0 ) {
- X ioctl( fd, TIOCNOTTY, (int*)0 );
- X close( fd );
- X }
- X#else
- X (void) setpgrp();
- X while( (pid = fork()) < 0 ) {
- X sleep( 2 );
- X }
- X if( pid ) {
- X exit( 0 );
- X }
- X#endif
- X /* Put our pid in the lock file for death detection */
- X if( (fp_lock = fopen( file_exp( "%X/LOCKmthreads" ), "w" )) != Nullfp ) {
- X fprintf( fp_lock, "%d\n", getpid() );
- X fclose( fp_lock );
- X }
- X
- X log_entry( "mthreads daemon started.\n" );
- X
- X#ifndef SERVER
- X last_modified = 0;
- X#endif
- X sigset( SIGALRM, alarm_handler );
- X
- X /* Start timer -- first interval is shorter than all others */
- X alarm( TIMER_FIRST );
- X for( ;; ) {
- X if( caught_interrupt ) {
- X wrap_it_up( 0 );
- X /* NORETURN */
- X }
- X pause(); /* let alarm go off */
- X if( caught_interrupt ) {
- X wrap_it_up( 0 );
- X /* NORETURN */
- X }
- X alarm( 0 );
- X
- X /* Re-open our log file, if needed */
- X if( !fp_log && !(fp_log = fopen( file_exp( "%X/mt.log" ), "a" )) ) {
- X wrap_it_up( 1 );
- X }
- X#ifndef SERVER
- X if( stat( file_exp( ACTIVE ), &filestat ) < 0 ) {
- X log_entry( "Unable to stat active file -- quitting.\n" );
- X wrap_it_up( 1 );
- X }
- X#endif
- X if( expire_time && time( 0L ) > expire_time ) {
- X expire_time += 24L * 60 * 60;
- X extra_expire = TRUE;
- X }
- X#ifdef SERVER
- X if( 1 ) { /* always compare files */
- X#else
- X if( extra_expire || filestat.st_mtime != last_modified ) {
- X last_modified = filestat.st_mtime;
- X#endif
- X makethreads();
- X }
- X alarm( daemon_delay );
- X fclose( fp_log ); /* close the log file while we sleep */
- X fp_log = Nullfp;
- X } /* for */
- X }/* if */
- X
- X wrap_it_up( 0 );
- X}
- X
- XSIGRET
- Xalarm_handler()
- X{
- X sigset( SIGALRM, alarm_handler );
- X}
- X
- XSIGRET
- Xint_handler( sig )
- Xint sig;
- X{
- X /* Flag interrupt occurred -- main loop attempts an orderly retreat. */
- X if( ++caught_interrupt >= 3 ) {
- X wrap_it_up( 1 );
- X }
- X /* Re-open our log file, if needed */
- X if( fp_log || (fp_log = fopen( file_exp( "%X/mt.log" ), "a" )) ) {
- X if( sig == SIGTERM ) {
- X log_entry( "mthreads halted.\n", sig);
- X } else {
- X log_entry( "Interrupt %d received.\n", sig);
- X }
- X }
- X if( !daemon_delay ) {
- X printf( "interrupt %d!\n", sig );
- X }
- X}
- X
- X/* Severe interrupts require severe action -- abort immediately, possibly
- X** removing the thread file on the way.
- X*/
- XSIGRET
- Xsevere_handler( sig )
- Xint sig;
- X{
- X /* Let's be a bit paranoid here -- avoid any possibility of looping. */
- X if( caught_interrupt >= 10 ) {
- X wrap_it_up( 1 );
- X }
- X caught_interrupt = 10;
- X
- X /* Destroy offending thread file if requested to do so. */
- X if( zap_thread ) {
- X unlink( thread_name( line ) );
- X }
- X /* Re-open our log file, if needed */
- X if( fp_log || (fp_log = fopen( file_exp( "%X/mt.log" ), "a" )) ) {
- X log_error( "** Severe signal: %d **\n", sig );
- X if( zap_thread ) {
- X log_entry( "Destroyed thread file for %s\n", line );
- X }
- X }
- X if( !daemon_delay ) {
- X printf( "Severe signal: %d!\n", sig);
- X if( zap_thread ) {
- X printf( "Destroyed thread file for %s\n", line );
- X }
- X }
- X wrap_it_up( 1 );
- X}
- X
- Xvoid
- Xwrap_it_up( ret )
- Xint ret;
- X{
- X unlink( file_exp( "%X/LOCKmthreads" ) ); /* remove lock */
- X
- X exit( ret );
- X}
- X
- X/* Process the active file, creating/modifying the active2 file and
- X** creating/modifying the thread data files.
- X*/
- Xvoid
- Xmakethreads()
- X{
- X register char *cp, *cp2;
- X FILE *fp_active, *fp_active2, *fp_active3;
- X long first, last, first2, last2;
- X char ch, ch2;
- X char data_file_open;
- X bool update_successful;
- X bool eof_active = FALSE, eof_active2 = FALSE;
- X
- X#ifdef SERVER
- X switch( server_init( server ) ) {
- X case OK_NOPOST:
- X case OK_CANPOST:
- X break;
- X case ERR_ACCESS:
- X log_entry( "Server %s rejected connection -- quitting.\n", server );
- X wrap_it_up( 1 );
- X default:
- X log_entry( "Couldn't connect with server %s -- sleeping.\n", server );
- X return;
- X }
- X put_server( "LIST" ); /* ask server for the active file */
- X get_server( line, sizeof line );
- X if( *line != CHAR_OK ) {
- X log_entry( "Unable to get active file from server -- sleeping.\n" );
- X close_server();
- X return;
- X }
- X if( (fp_active = fopen( file_exp( ACTIVE1 ), "w+" )) == Nullfp ) {
- X log_entry( "Unable to write the active1 file.\n" );
- X wrap_it_up( 1 );
- X }
- X while( 1 ) {
- X if( caught_interrupt ) {
- X wrap_it_up( 0 );
- X /* NORETURN */
- X }
- X if( get_server( line, sizeof line ) < 0 ) {
- X log_entry( "Server failed to send entire active file -- sleeping.\n" );
- X fclose( fp_active );
- X close_server();
- X return;
- X }
- X if( *line == '.' ) {
- X break;
- X }
- X fputs( line, fp_active );
- X putc( '\n', fp_active );
- X }
- X fseek( fp_active, 0L, 0 ); /* rewind for read */
- X#else
- X if( (fp_active = fopen( file_exp( ACTIVE ), "r" )) == Nullfp ) {
- X log_entry( "Unable to open the active file.\n" );
- X wrap_it_up( 1 );
- X }
- X#endif
- X filename = file_exp( ACTIVE2 );
- X if( (fp_active3 = fopen( filename, "r+" )) == Nullfp ) {
- X if( (fp_active3 = fopen( filename, "w" )) == Nullfp ) {
- X log_entry( "Unable to open the active2 file for update.\n" );
- X wrap_it_up( 1 );
- X }
- X }
- X if( (fp_active2 = fopen( filename, "r" )) == Nullfp ) {
- X log_entry( "Unable to open the active2 file.\n" );
- X wrap_it_up( 1 );
- X }
- X if( caught_interrupt ) {
- X wrap_it_up( 0 );
- X /* NORETURN */
- X }
- X if( extra_expire && log_verbosity ) {
- X log_entry( "Using enhanced expiration for this pass.\n" );
- X }
- X
- X processed_groups = added_groups = removed_groups = 0;
- X added_articles = expired_articles = 0;
- X
- X /* Loop through entire active file. */
- X for( ;; ) {
- X if( eof_active || !fgets( line, sizeof line, fp_active ) ) {
- X if( eof_active2 && !line_root ) {
- X break;
- X }
- X eof_active = TRUE;
- X ch = 'x';
- X } else {
- X if( !(cp = index( line, ' ' )) ) {
- X log_entry( "active line has no space: %s\n", line );
- X continue;
- X }
- X *cp = '\0';
- X if( sscanf( cp+1, "%ld %ld %c", &last, &first, &ch ) != 3 ) {
- X log_entry( "active digits corrupted: %s %s\n", line, cp+1 );
- X continue;
- X }
- X }
- X if( debug || log_verbosity > 3 ) {
- X log_entry( "Processing %s:\n", line );
- X }
- X data_file_open = 0;
- X /* If we've allocated some lines in memory while searching for
- X ** newsgroups (they've scrambled the active file on us), check
- X ** them first.
- X */
- X last_line = Nullact;
- X for( pline = line_root; pline; pline = pline->link ) {
- X if( eof_active || strEQ( line, pline->name ) ) {
- X strcpy( line2, pline->name );
- X free( pline->name );
- X first2 = pline->first;
- X last2 = pline->last;
- X ch2 = pline->type;
- X if( last_line ) {
- X last_line->link = pline->link;
- X } else {
- X line_root = pline->link;
- X }
- X free( pline );
- X break;
- X }
- X last_line = pline;
- X }/* for */
- X /* If not found yet, check the active2 file. */
- X if( !pline ) {
- X for( ;; ) {
- X if( eof_active2 || !fgets( line2, sizeof line2, fp_active2 ) ) {
- X /* At end of file, check if the thread data file exists.
- X ** If so, use its high/low values. Else, default to
- X ** some initial values.
- X */
- X eof_active2 = TRUE;
- X if( eof_active ) {
- X break;
- X }
- X strcpy( line2, line );
- X if( (data_file_open = init_data( thread_name( line ) )) ) {
- X last2 = total.last;
- X first2 = total.first;
- X ch2 = 'y';
- X } else {
- X total.first = first2 = first;
- X if( add_new ) {
- X total.last = last2 = first - 1;
- X ch2 = (ch == '=' ? 'x' : ch);
- X added_groups++;
- X } else {
- X total.last = last2 = last;
- X ch2 = (ch == '=' ? 'X' : toupper( ch ));
- X }
- X }
- X data_file_open++; /* (1 == empty, 2 == open) */
- X break;
- X }
- X if( !(cp2 = index( line2, ' ' )) ) {
- X log_entry( "active2 line has no space: %s\n", line2 );
- X continue;
- X }
- X *cp2 = '\0';
- X if( sscanf( cp2+1,"%ld %ld %c",&last2,&first2,&ch2 ) != 3 ) {
- X log_entry( "active2 digits corrupted: %s %s\n",
- X line2, cp2+1 );
- X continue;
- X }
- X /* Check if we're still in-sync */
- X if( eof_active || strEQ( line, line2 ) ) {
- X break;
- X }
- X /* Nope, we've got to go looking for this line somewhere
- X ** down in the file. Save each non-matching line in memory
- X ** as we go.
- X */
- X pline = (ACTIVE_LINE*)safemalloc( sizeof (ACTIVE_LINE) );
- X pline->name = savestr( line2 );
- X pline->last = last2;
- X pline->first = first2;
- X pline->type = ch2;
- X pline->link = Nullact;
- X if( !last_line ) {
- X line_root = pline;
- X } else {
- X last_line->link = pline;
- X }
- X last_line = pline;
- X }/* for */
- X if( eof_active && eof_active2 ) {
- X break;
- X }
- X }/* if !pline */
- X if( eof_active ) {
- X strcpy( line, line2 );
- X if( truncate_len < 0 ) {
- X truncate_len = ftell( fp_active3 );
- X }
- X }
- X if( rebuild ) {
- X unlink( thread_name( line ) );
- X }
- X update_successful = FALSE;
- X if( hierarchy_list ) {
- X action = ngmatch( hierarchy_list, line );
- X } else {
- X action = NG_DEFAULT;
- X }
- X switch( action ) {
- X case NG_DEFAULT:
- X if( ch2 < 'a' ) {
- X action = NG_SKIP;
- X } else {
- X action = NG_MATCH;
- X }
- X break;
- X case NG_MATCH: /* add if unthreaded */
- X if( ch2 < 'a' ) {
- X total.last = last2 = first2 - 1;
- X added_groups++;
- X }
- X break;
- X case NG_SKIP: /* remove if threaded */
- X if( ch2 >= 'a' && !debug ) {
- X unlink( thread_name( line ) );
- X removed_groups++;
- X }
- X break;
- X }
- X if( caught_interrupt || (debug && action != NG_MATCH) ) {
- X dont_read_data( data_file_open ); /* skip silently */
- X } else if( ch == 'x' || ch == '=' ) {
- X if( !daemon_delay ) { /* skip 'x'ed groups */
- X putchar( 'x' );
- X }
- X ch = (action == NG_SKIP ? 'X' : 'x');
- X if( (ch2 >= 'a' && ch2 != 'x') || force_flag ) {
- X /* Remove thread file if group is newly 'x'ed out */
- X unlink( thread_name( line ) );
- X }
- X update_successful = TRUE;
- X dont_read_data( data_file_open );
- X } else if( action == NG_SKIP ) { /* skip excluded groups */
- X if( !daemon_delay ) {
- X putchar( 'X' );
- X }
- X ch = toupper( ch );
- X if( force_flag ) {
- X unlink( thread_name( line ) );
- X }
- X update_successful = TRUE;
- X dont_read_data( data_file_open );
- X } else if( no_processing ) {
- X if( !daemon_delay ) {
- X putchar( ',' );
- X }
- X ch2 = ch;
- X dont_read_data( data_file_open );
- X } else if( !force_flag && !extra_expire && !rebuild
- X && first == first2 && last == last2 ) {
- X /* We're up-to-date here. Skip it. */
- X if( !daemon_delay ) {
- X putchar( '.' );
- X }
- X update_successful = TRUE;
- X dont_read_data( data_file_open );
- X } else {
- X /* Looks like we need to process something. */
- X#ifdef SERVER
- X sprintf( line2, "GROUP %s", line );
- X put_server( line2 ); /* go to next group */
- X if( get_server( line2, sizeof line2 ) < 0 || *line2 != CHAR_OK ) {
- X log_entry( "NNTP failure on group `%s'.\n", line );
- X#else
- X cp = line2;
- X while( (cp = index( cp, '.' )) ) {
- X *cp = '/';
- X }
- X filename = file_exp( line2 ); /* relative to spool dir */
- X if( chdir( filename ) < 0 ) {
- X if (errno != ENOENT) {
- X log_entry( "Unable to chdir to `%s'.\n", filename );
- X }
- X#endif
- X if( !daemon_delay ) {
- X putchar( '*' );
- X }
- X dont_read_data( data_file_open );
- X } else {
- X filename = thread_name( line );
- X /* Try to open the data file only if we didn't try it
- X ** in the name matching code above.
- X */
- X if( !data_file_open-- ) { /* (0 == haven't tried yet) */
- X if( !(data_file_open = init_data( filename )) ) {
- X total.last = first - 1;
- X total.first = first;
- X }
- X }
- X strcpy( line2, filename );
- X cp = rindex( line2, '/' ) + 1;
- X
- X if( data_file_open ) { /* (0 == empty, 1 == open) */
- X if( !read_data() ) { /* did read fail? */
- X#ifndef DEBUG
- X unlink( filename ); /* trash input file */
- X#else
- X strcpy( cp, "bad.read" );
- X rename( filename, line2 );
- X#endif
- X data_file_open = init_data( filename );
- X total.last = first - 1;
- X total.first = first;
- X }
- X }
- X grevious_error = FALSE;
- X process_articles( first, last );
- X processed_groups++;
- X if( caught_interrupt ) {
- X processed_groups--; /* save nothing -- no update */
- X } else if( !added_count && !expired_count && last == last2 ) {
- X (void) write_data( Nullch );
- X if( !daemon_delay ) {
- X putchar( ':' );
- X }
- X update_successful = TRUE;
- X } else if( !total.root ) {
- X /* When the data file goes empty, remove it. */
- X unlink( filename );
- X expired_articles += expired_count;
- X if( !daemon_delay ) {
- X putchar( '-' );
- X }
- X update_successful = TRUE;
- X } else {
- X strcpy( cp, NEW_THREAD ); /* write data as .new */
- X if( write_data( line2 ) && !grevious_error ) {
- X rename( line2, filename );
- X added_articles += added_count;
- X expired_articles += expired_count;
- X if( !daemon_delay ) {
- X putchar( '#' );
- X }
- X update_successful = TRUE;
- X } else {
- X#ifndef DEBUG
- X unlink( line2 ); /* blow-away bad write */
- X#else
- X cp = rindex( filename, '/' ) + 1;
- X strcpy( cp, "bad.write" );
- X rename( line2, filename );
- X#endif
- X if( !daemon_delay ) {
- X putchar( '!' );
- X }
- X }/* if */
- X }/* if */
- X }/* if */
- X }/* if */
- X /* Finally, update the active2 entry for this newsgroup. */
- X if( update_successful ) {
- X fprintf( fp_active3, fmt_active2, line, last, first, ch );
- X } else {
- X fprintf( fp_active3, fmt_active2, line, last2, first2, ch2 );
- X }
- X /* If we're not out of sync, keep active2 file flushed. */
- X if( !line_root ) {
- X fflush( fp_active3 );
- X }
- X }/* for */
- X
- X#ifdef SERVER
- X close_server();
- X#endif
- X fclose( fp_active );
- X fclose( fp_active2 );
- X fclose( fp_active3 );
- X
- X if( truncate_len >= 0 ) {
- X#ifdef TRUNCATE
- X if( truncate( file_exp( ACTIVE2 ), truncate_len ) == -1 )
- X log_entry( "Unable to truncate the active2 file.\n" );
- X#else
- X#ifdef CHSIZE
- X int fd;
- X if( (fd = open( file_exp( ACTIVE2 ), O_RDWR )) == -1 )
- X log_entry( "Unable to open the active2 file for truncation.\n" );
- X else {
- X if( chsize( fd, truncate_len ) == -1 )
- X log_entry( "Unable to truncate the active2 file.\n" );
- X close( fd );
- X }
- X#else
- X filename = file_exp( ACTIVE2 );
- X sprintf( line, "%s.new", filename );
- X if( (fp_active3 = fopen( line, "w" )) == Nullfp ) {
- X log_entry( "Unable to create the active2.new file.\n" );
- X } else if( (fp_active2 = fopen( filename, "r" )) == Nullfp ) {
- X fclose( fp_active3 );
- X unlink( line );
- X log_entry( "Unable to open the active2 file.\n" );
- X } else {
- X while( ftell( fp_active3 ) < truncate_len ) {
- X if( !fgets( line2, sizeof line2, fp_active2 ) ) {
- X break;
- X }
- X fputs( line2, fp_active3 );
- X }
- X sprintf( line2, "%s.old", filename );
- X rename( filename, line2 );
- X rename( line, filename );
- X fclose( fp_active2 );
- X fclose( fp_active3 );
- X }
- X#endif /* not XENIX */
- X#endif /* not TRUNCATE */
- X truncate_len = -1;
- X }
- X
- X sprintf( line, "Processed %d group%s: added %d article%s, expired %d.\n",
- X processed_groups, processed_groups == 1 ? nullstr : "s",
- X added_articles, added_articles == 1 ? nullstr : "s",
- X expired_articles );
- X
- X if( processed_groups ) {
- X log_entry( line );
- X }
- X
- X if( !daemon_delay ) {
- X putchar( '\n' );
- X fputs( line, stdout );
- X }
- X if( added_groups ) {
- X sprintf( line, "Turned %d group%s on.\n", added_groups,
- X added_groups == 1 ? nullstr : "s" );
- X log_entry( line );
- X if( !daemon_delay ) {
- X fputs( line, stdout );
- X }
- X }
- X if( removed_groups ) {
- X sprintf( line, "Turned %d group%s off.\n", removed_groups,
- X removed_groups == 1 ? nullstr : "s" );
- X log_entry( line );
- X if( !daemon_delay ) {
- X fputs( line, stdout );
- X }
- X }
- X extra_expire = FALSE;
- X rebuild = FALSE;
- X}
- X
- X/*
- X** ngmatch - newsgroup name matching
- X**
- X** returns NG_MATCH for a positive patch, NG_SKIP for a negative match,
- X** and NG_DEFAULT if the group doesn't match at all.
- X**
- X** "all" in a pattern is a wildcard that matches exactly one word;
- X** it does not cross "." (NGDELIM) delimiters.
- X**
- X** This matching code was borrowed from C news.
- X*/
- X
- X#define ALL "all" /* word wildcard */
- X
- X#define NGNEG '!'
- X#define NGSEP ','
- X#define NGDELIM '.'
- X
- Xint
- Xngmatch( ngpat, grp )
- Xchar *ngpat, *grp;
- X{
- X register char *patp; /* point at current pattern */
- X register char *patcomma;
- X register int depth;
- X register int faildeepest = 0, hitdeepest = 0; /* in case no match */
- X register bool negation;
- X
- X for( patp = ngpat; patp != Nullch; patp = patcomma ) {
- X negation = FALSE;
- X patcomma = index( patp, NGSEP );
- X if( patcomma != Nullch ) {
- X *patcomma = '\0'; /* will be restored below */
- X }
- X if( *patp == NGNEG ) {
- X ++patp;
- X negation = TRUE;
- X }
- X depth = onepatmatch( patp, grp ); /* try 1 pattern, 1 group */
- X if( patcomma != Nullch ) {
- X *patcomma++ = NGSEP; /* point after the comma */
- X }
- X if( depth == 0 ) { /* mis-match */
- X ; /* ignore it */
- X } else if( negation ) {
- X /* record depth of deepest negated matched word */
- X if( depth > faildeepest ) {
- X faildeepest = depth;
- X }
- X } else {
- X /* record depth of deepest plain matched word */
- X if( depth > hitdeepest ) {
- X hitdeepest = depth;
- X }
- X }
- X }
- X if( hitdeepest > faildeepest ) {
- X return NG_MATCH;
- X } else if( faildeepest ) {
- X return NG_SKIP;
- X } else {
- X return NG_DEFAULT;
- X }
- X}
- X
- X/*
- X** Match a pattern against a group by looking at each word of pattern in turn.
- X**
- X** On a match, return the depth (roughly, ordinal number * k) of the rightmost
- X** word that matches. If group runs out first, the match fails; if pattern
- X** runs out first, it succeeds. On a failure, return zero.
- X*/
- Xint
- Xonepatmatch( patp, grp )
- Xchar *patp, *grp;
- X{
- X register char *rpatwd; /* used by word match (inner loop) */
- X register char *patdot, *grdot; /* point at dots after words */
- X register char *patwd, *grwd; /* point at current words */
- X register int depth = 0;
- X
- X for( patwd = patp, grwd = grp;
- X patwd != Nullch && grwd != Nullch;
- X patwd = patdot, grwd = grdot
- X ) {
- X register bool match = FALSE;
- X register int incr = 20;
- X
- X /* null-terminate words */
- X patdot = index(patwd, NGDELIM);
- X if( patdot != Nullch ) {
- X *patdot = '\0'; /* will be restored below */
- X }
- X grdot = index( grwd, NGDELIM );
- X if( grdot != Nullch ) {
- X *grdot = '\0'; /* will be restored below */
- X }
- X /*
- X * Match one word of pattern with one word of group.
- X * A pattern word of "all" matches any group word,
- X * but isn't worth as much.
- X */
- X#ifdef FAST_STRCMP
- X match = STREQ( patwd, grwd );
- X if( !match && STREQ( patwd, ALL ) ) {
- X match = TRUE;
- X --incr;
- X }
- X#else
- X for( rpatwd = patwd; *rpatwd == *grwd++; ) {
- X if( *rpatwd++ == '\0' ) {
- X match = TRUE; /* literal match */
- X break;
- X }
- X }
- X if( !match ) {
- X /* ugly special case match for "all" */
- X rpatwd = patwd;
- X if( *rpatwd++ == 'a' && *rpatwd++ == 'l'
- X && *rpatwd++ == 'l' && *rpatwd == '\0' ) {
- X match = TRUE;
- X --incr;
- X }
- X }
- X#endif /* FAST_STRCMP */
- X
- X if( patdot != Nullch ) {
- X *patdot++ = NGDELIM; /* point after the dot */
- X }
- X if( grdot != Nullch ) {
- X *grdot++ = NGDELIM;
- X }
- X if( !match ) {
- X depth = 0; /* words differed - mismatch */
- X break;
- X }
- X depth += incr;
- X }
- X /* if group name ran out before pattern, then match fails */
- X if( grwd == Nullch && patwd != Nullch ) {
- X depth = 0;
- X }
- X return depth;
- X}
- X
- X/* Generate a log entry with timestamp.
- X*/
- X/*VARARGS1*/
- Xvoid
- Xlog_entry( fmt, arg1, arg2 )
- Xchar *fmt;
- Xlong arg1;
- Xlong arg2;
- X{
- X time_t now;
- X char *ctime();
- X
- X (void) time( &now );
- X fprintf( fp_log, "%.12s ", ctime( &now )+4 );
- X fprintf( fp_log, fmt, arg1, arg2 );
- X fflush( fp_log );
- X}
- X
- X/* Generate an log entry, with 'E'rror flagging (non-daemon mode), time-stamp,
- X** and newsgroup name.
- X*/
- X/*VARARGS1*/
- Xvoid
- Xlog_error( fmt, arg1, arg2, arg3 )
- Xchar *fmt;
- Xlong arg1;
- Xlong arg2;
- Xlong arg3;
- X{
- X log_entry( "%s: ", line );
- X fprintf( fp_log, fmt, arg1, arg2, arg3 );
- X fflush( fp_log );
- X if( *fmt == '*' ) {
- X grevious_error = TRUE;
- X if( !daemon_delay ) {
- X putchar( 'E' );
- X }
- X }
- X else {
- X if( !daemon_delay ) {
- X putchar( 'e' );
- X }
- X }
- X}
- X
- X#ifndef RENAME
- Xint
- Xrename( old, new )
- Xchar *old, *new;
- X{
- X struct stat st;
- X
- X if( stat( old, &st ) == -1 ) {
- X return -1;
- X }
- X if( unlink( new ) == -1 && errno != ENOENT ) {
- X return -1;
- X }
- X if( link( old, new ) == -1 ) {
- X return -1;
- X }
- X if( unlink( old ) == -1 ) {
- X int e = errno;
- X (void) unlink( new );
- X errno = e;
- X return -1;
- X }
- X return 0;
- X}
- X#endif /*RENAME*/
- END_OF_FILE
- if test 29761 -ne `wc -c <'mthreads.c'`; then
- echo shar: \"'mthreads.c'\" unpacked with wrong size!
- fi
- # end of 'mthreads.c'
- fi
- echo shar: End of archive 6 \(of 14\).
- cp /dev/null ark6isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 14 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
-